
'''
@File    :   Set_up.py
@Time    :   2022/04/13 18:00:00
@Author  :   WangWei 
@Version :   1.0
@Contact :   wangwei@chengtech.com
@License :   (C)Copyright 2022-2025
@Desc    :   None
'''

# here put the import lib
from pytest_assume.plugin import assume
 
import pytest
import pandas
import numpy
import logging
import time
from commDriver.modbus_plc import *
from commDriver.TS_CAN import *
import allure
import sys
import Set_up
from collections.abc import Iterable
import crcmod
import bincopy
import math
import binascii
from ctypes import *
import logging
from struct import *
import time
import os, sys
import datetime
import csv
import numpy as np


FACTORY_CAN_SET_WORK_REQ = 0x100
FACTORY_DBF_WRITE_REQ = 0x154
FACTORY_CAN_SET_WORK_ANS = 0x200
FACTORY_CAN_OEM_VERSION_REQ = 0x103
FACTORY_CAN_OEM_SN_REQ = 0x105
FACTORY_CAN_OEM_VERSION_ANS = 0x203
FACTORY_BURN_CFG_REQ = 0x116
FACTORY_BURN_CFG_ANS = 0x216
FACTORY_CAN_SEND_WAVE_REQ = 0x153
FACTORY_TPS_CALIB_CFG_REQ = 0x156
FACTORY_DBF_CMD_REQ = 0x154

CAN_SHELL_CMD_FRAME_ID = 0x114
FACTORY_CAN_OEM_SN_ANS = 0x205
FACTORY_CAN_R_PA_CAL_ANS = 0x213
CAN_SHELL_CMD_FRAME_ID_ACK = 0x214
FACTORY_TPS_CALIB_CFG_ANS = 0x256
FACTORY_CAN_SEND_WAVE_ANS = 0x253
FACTORY_DBF_CMD_ANS = 0x254
FACTORY_TPS_PHASE_CODE_ANS = 0x257

canDataAry = c_char * 8
canDataObjAry24 = c_char * 24
rxMsgs = []
rxMsgIdEnableFilterList = []  # 接收消息ID允许列表

crcTable = list(range(256))

class DbfV2(object):

    def SendFrame(self, Driver, ID,Data, CanType='CAN'):
        Driver['CAN2'].fush_q_buff()  # 清缓存
        Driver['CAN2'].send_Frame(ID,Data,CanType)

    def RecvFrame(self, Driver, ID, timeSleep = 0.08):
        time.sleep(timeSleep)
        ALl_data,buffer_size = Driver['CAN2'].read_frame()
        response_data = ALl_data[ALl_data['AIdentifier'] == ID]
        try:
            last_data = response_data.iloc[0]['ADatas'] 
        except:
            last_data = [0,0,0,0,0,0,0,0]
            logging.info('[0x{0:X}] no response'.format(ID))
        datas = []
        for i in range (8):
            datai = last_data[i]
            datas.append(datai)
        readstr = binascii.b2a_hex(bytearray(datas),' ')
        logging.info('readstr is :{}'.format(readstr))

        return datas

    def list_to_hex_string(self,list_data):
        list_str = '[ '
        for x in list_data:
            list_str += '0x{:02X} '.format(x)
        list_str += ']'
        return list_str

    def setupRecvFrameMsgIdEnableFilter(self, msgId, op):
        #lock.acquire()
        if op == 'clear':
            rxMsgIdEnableFilterList.clear()
            logging.info('禁止脚本接收任何msgID的报文')
        elif op == 'add':
            if msgId not in rxMsgIdEnableFilterList:
                rxMsgIdEnableFilterList.append(msgId)
                logging.info('新增脚本接收 msgID [ 0x{:02X} ]的报文'.format(msgId))
        elif op == 'del':
            if msgId in rxMsgIdEnableFilterList:
                rxMsgIdEnableFilterList.remove(msgId)
                logging.info('禁止脚本接收 msgID [ 0x{:02X} ]的报文'.format(msgId))
        #logging.info('当前允许接收的消息ID列表:', end=' ')
        #for msgId in rxMsgIdEnableFilterList:
        #    logging.info('[{}]'.format(hex(msgId)), end=' ')
        #logging.info('')
        #lock.release()
        return

    def getRxFrame(self):
        global lock
        lock.acquire()
        msg = rxMsgs.copy()
        rxMsgs.clear()
        lock.release()
        #for m in msg:
            #if m.ID == 0x114:
                # logging.info([hex(x) for x in m.Data[:8]])
                #recvCmd(m.Data)
        return msg


    def generateCrcTable(self):
        POLYNOMIAL = 0x04c11db7
        for i in range(256):
            crc_accum = i << 24
            for j in range(8):
                if crc_accum & 0x80000000:
                    crc_accum = ((crc_accum << 1) & 0xFFFFFFFF) ^ POLYNOMIAL
                else:
                    crc_accum = ((crc_accum << 1) & 0xFFFFFFFF)
            crcTable[i] = crc_accum
        return

    def calCRC(self, data):
        crc_accum = 0
        idx = 0
        for v in data:
            idx += 1
            i = ((crc_accum >> 24) ^ v) & 0xFF
            crc_accum = ((crc_accum << 8) & 0xFFFFFFFF) ^ crcTable[i]
        return crc_accum

    '''
        CANShell接口
    '''
    def CANShellTxCmdProc(self,Driver,cmd):
        logging.info(cmd)
        cnt = len(cmd)
        data = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
        data[0] = 0x88
        data[1] = (cnt >> 8) & 0xff
        data[2] = cnt & 0xff
        self.SendFrame(Driver, CAN_SHELL_CMD_FRAME_ID, data)

        idx = 0
        remain = cnt % 7
        bcmd = cmd.encode()
        for i in range(0, cnt - remain, 7):
            data[0] = idx
            data[1:] = bcmd[i:i + 7]
            self.SendFrame(Driver, CAN_SHELL_CMD_FRAME_ID, data)
            idx += 1

        if remain > 0:
            data[0] = idx
            data[1:remain + 1] = bcmd[cnt - remain:cnt]
            self.SendFrame(Driver, CAN_SHELL_CMD_FRAME_ID, data)

        data[0] = 0xff
        self.SendFrame(Driver, CAN_SHELL_CMD_FRAME_ID, data)


    def wCfgModeByCanShell(self, Driver, cfgMode):
        realSendCmdStr = 'cfg_mode' + ' ' + str(cfgMode)
        self.CANShellTxCmdProc(Driver, realSendCmdStr)
        return

    def wRadarCfgToFlashByCanShell(self, Driver):
        realSendCmdStr = 'cfg_to_flash'
        self.CANShellTxCmdProc(Driver, realSendCmdStr)
        return

    '''
        写入决定是否使用DBF因子
        [0]:使用 [1]:不使用DBF因子，使用天线间距相位差
    '''
    def wCaliModeByCanShell(self, Driver, caliMode0, caliMode1, caliMode2, caliMode3):
        realSendCmdStr = 'ant_cali_mode' + ' ' + str(caliMode0) + ' ' + str(caliMode1) + ' ' + str(caliMode2) + ' ' + str(
            caliMode3)
        self.CANShellTxCmdProc(Driver, realSendCmdStr)
        return

    '''
        保存校准版本号并写入Flash
        校准版本号影响是否进入调用DBF因子的地方
    '''
    def dutModeWriteCalVerAndSaveToFlash(self, Driver,calVerH, calVerL):
        self.setupRecvFrameMsgIdEnableFilter(FACTORY_CAN_OEM_VERSION_ANS, 'add')
        self.setupRecvFrameMsgIdEnableFilter(FACTORY_BURN_CFG_ANS, 'add')
        ret = False
        # 写入校准版本
        # read:1 write:0  0:NUL 1:HW_VER 2:SW_VER 3:CALI_VER
        txCmd = [0x03, calVerH, calVerL, 0x00, 0x00, 0x00, 0x00, 0x00]
        self.SendFrame(Driver, FACTORY_CAN_OEM_VERSION_REQ, txCmd)
        logging.info("写入校准版本[0x%x][0x%x]" % (calVerH, calVerL))
        # time.sleep(0.3)
        rxCmd = self.RecvFrame(Driver,FACTORY_CAN_OEM_VERSION_ANS)
        value = rxCmd[:8]
        # logging.info('雷达返回报文: [0x%x] %s' %(rxCmd[i].ID, list_to_hex_string(value)))
        if (value[0] == 0x83):
            logging.info("写入校准版本成功")
            ret = True

        if ret == False:
            logging.info("写入校准版本失败")
            return False

        txCmd.clear()
        txCmd = [0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
        self.SendFrame(Driver, FACTORY_BURN_CFG_REQ, txCmd)
        logging.info("保存至Flash")
        time.sleep(1)  # 如果是venus开发板，1秒等待；正常雷达，300ms即可
        ret = False
        rxCmd = self.RecvFrame(Driver,FACTORY_BURN_CFG_ANS)
        value = rxCmd[:8]
        #logging.info('雷达返回报文: [0x%x] %s' % (rxCmd[i].ID, list_to_hex_string(value)))
        if (value[0] == 0x01) and (value[1] == 0x11):
            logging.info("校准版本保存至Flash成功")
            ret = True

        if ret == False:
            logging.info("校准版本保存至Flash失败")
            return False
        return True
    '''
    DUT模式接口:安全模式访问请求，返回随机数
    '''
    def dutModeSecurityAccessRequestProc(self, Driver):
        txCmd = [0x0E, 0x31, 0x58, 0xAF, 0x01, 0x02, 0x03, 0x4C]
        self.SendFrame(Driver, FACTORY_CAN_SET_WORK_REQ, txCmd)
        logging.info("发送安全认证请求")
        # time.sleep(0.2)
        value = self.RecvFrame(Driver, FACTORY_CAN_SET_WORK_ANS)
        
        if (value[0] == 0x0E) and (value[1] == 0x31) and (
                value[2] == 0x58) and (value[3] == 0xAF):
            logging.info("安全请求成功，回复雷达安全确认")

        if len(value) > 0:
            dutCheckSum = (0x0F + 0x31 + 0x58 + 0xAF + value[4] + value[5] + value[6]) & 0xFF
            txCmd = [0x0F, 0x31, 0x58, 0xAF, value[4], value[5], value[6], dutCheckSum]
            randomNum = value[4] | value[5] << 8 | value[6] << 24
            self.SendFrame(Driver, FACTORY_CAN_SET_WORK_REQ, txCmd)
            # time.sleep(0.2)
            rxCmdsList = self.RecvFrame(Driver, FACTORY_CAN_SET_WORK_ANS)

            value = rxCmdsList[:8]
            #logging.info('雷达返回报文: [0x%x] %s' %(rxCmd[i].ID, list_to_hex_string(value)))
            if (value[0] == 0x0F) and (value[1] == 0x31) and (
                    value[2] == 0x58) and (value[3] == 0xAF) and (value[4] == 0x00):
                logging.info("安全请求确认成功，访问随机数【0x{:08x}】".format(randomNum))
                return True
        else:
            logging.info("安全请求确认失败，未接收到回复的访问随机数")
        return False


    '''
    DUT模式接口:进入指定dut模式【1：普通模式 2：2DFFT校准模式 3：天线相位偏移校准模式】
    '''
    def dutModeSpecWorkModeRequestProc(self, Driver, specDutMode):
        ret = False
        txCmd = [specDutMode, 0x31, 0x58, 0xAF, 0x00, 0x00, 0x00, 0x00]
        self.SendFrame(Driver, FACTORY_CAN_SET_WORK_REQ, txCmd)
        # time.sleep(0.2)
        rxCmdsList = self.RecvFrame(Driver, FACTORY_CAN_SET_WORK_ANS)
        value = rxCmdsList[:8]
        #logging.info('雷达返回报文: [0x%x] %s' %(rxCmd[i].ID, list_to_hex_string(value)))
        if (value[0] == 0x01) and (value[1] == 0x31) and (
                value[2] == 0x58) and (value[3] == 0xAF):
                logging.info("进入生产模式【{}】模式成功".format(specDutMode))
                ret = True
        if ret == False:
            logging.info("进入生产模式【{}】模式失败".format(specDutMode))
            return False
        return True

    def to_int(self, str):
        try:
            int(str)
            return int(str)
        except ValueError:  # 报类型错误，说明不是整型的
            try:
                float(str)  # 用这个来验证，是不是浮点字符串
                return int(float(str))
            except ValueError:  # 如果报错，说明即不是浮点，也不是int字符串。   是一个真正的字符串
                return False

    def swapBitOrder(n):
        binary = bin(n)[2:]  # 将整数转换为二进制字符串，并去掉开头的 '0b'
        reversed_binary = binary[::-1]  # 反转二进制字符串
        swapped = int(reversed_binary, 2)  # 将反转后的二进制字符串转换回整数
        return swapped

    '''
        从csv文件加载dbf因子数据
        dbf因子数据按行排列，一行代表一个profile的数据
        输入参数
            [1]dbfFactorCsvFilePath:保存DBF因子的CSV文件
    '''
    def loadDbfFactorFromCsvFile(self, dbfFactorCsvFilePath):
        logging.info('DBF因子文件路径[%s]' % (dbfFactorCsvFilePath))
        dbfFactorData = []
        with open(dbfFactorCsvFilePath, 'r', encoding='utf-8-sig') as file:
            csv_reader = csv.reader(file)
            lineIdx = 0
            for row in csv_reader:
                logging.info('lineIdx[%d] dbfFactorNum[%d]' % (lineIdx, len(row)))
                dbfFactorData.append([])
                for col in range(len(row)):
                    a = self.to_int(row[col])
                    # if lineIdx == 1:
                    #     dbfFactorData[lineIdx].append(88888888)
                    # else:
                    dbfFactorData[lineIdx].append(a)
                lineIdx = lineIdx + 1
        return dbfFactorData

    '''
        检测dbf因子区域CRC
        输入参数:
            [1]profileIdx：DBF因子序号【这里不用flash地址因为字节数不足】
            [2]partitionCRC：该分区DBF因子由上位机计算的CRC值
        输出参数:
            [1]成功/失败：下位机上报的CRC与上位机计算的CRC比对结果
            [2]ansCRC：上位机上报的CRC值
    '''
    def checkSingleDbfFactorPartitionCRC(self, Driver, profileIdx=0, partitionFlashAddr=0, partitionCRC=0):
        ansCRC = 0
        txCmd = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
        txCmd[0] = (0x80 | 0x4)
        txCmd[1] = profileIdx
        txCmd[2] = (partitionFlashAddr >> 24) & 0XFF
        txCmd[3] = (partitionFlashAddr >> 16) & 0XFF
        txCmd[4] = (partitionFlashAddr >>  8) & 0XFF
        txCmd[5] = partitionFlashAddr & 0XFF
        txCmd[6] = 0
        txCmd[7] = 0

        self.SendFrame(Driver, FACTORY_DBF_CMD_REQ, txCmd)
        # time.sleep(0.5)
        rxCmdList = self.RecvFrame(Driver, FACTORY_DBF_CMD_ANS)
        if len(rxCmdList) > 0:
            if (rxCmdList[0] == 0x84):  # 0x84 CRC command
                # logging.info('雷达返回报文: [0x%x] %s' % (rxCmd.ID, list_to_hex_string(list(rxCmdList))))
                cmdErrCode = (rxCmdList[1]) & 0x3F
                cmdResult = (rxCmdList[1] >> 6) & 0x03
                ansCRC = ((rxCmdList[2] << 24) | (rxCmdList[3] << 16) | (rxCmdList[4] << 8) | rxCmdList[5])
                if cmdResult == 1 and ansCRC == partitionCRC:
                    return True,  'DBF因子分区信息CRC比对正确', ansCRC
                else:
                    return False, 'DBF因子分区信息CRC比对错误', ansCRC
        else:
            logging.info('未收到雷达CRC校验返回报文')
        return False, '未执行DBF因子分区信息比对流程', 0

    '''
        生成DBF因子的分区信息，并拆分为字节
        输入参数
            [1]magicNum：4字节幻数，固定设置为【0x5A5A5A5A】 【兼容老版本DBF因子格式】
            [2]dbfPartitionBytesNum:该分区的DBF因子字节数，必须是4的整数倍，DBF因子数就是字节数//4 【兼容老版本DBF因子格式】
            [3]dbfPartitionCRC:该分区的DBF因子数据CRC校验值，注意该校验值只校验数据不校验分区信息 【兼容老版本DBF因子格式】
            [4]dbfPartitionProfile:该分区对应的Profile序号，为了实现Profile的相对自由配置 
                【老版本的DBF因子没有该字段，1字节】 【另外3字节存储版本号：主版本号1字节 次版本号2字节】
            [5]dbfPartitionInfoBytesNum:分区信息字节长度 默认256 
            此外，会对DBF因子的分区信息自身再做CRC 【dbfPartitionInfoCRC】 并保存至Flash 已保证分区信息的正确性
        返回参数:
            [1]长度为【256】字节的DBF因子分区信息，拆分为字节。注意需调整为Flash内的存储顺序
            
    '''
    def convertDbfFactorPartitionInfoToBytes(self, magicNum=0x5A5A5A5A, dbfPartitionBytesNum=0, dbfPartitionCRC=0, dbfPartitionProfile=0,
                                            dbfPartitionBlockNum=0, dbfPartitionInfoBytesNum=256, dbfPartitionInfoVersonMain=1,
                                            dbfPartitionInfoVersonSub=0):
        dbfPartitionInfoBytes = list([0] * dbfPartitionInfoBytesNum)
        dbfPartitionInfoBytesFlash = list([0] * dbfPartitionInfoBytesNum) #用于计算分区信息CRC
        #参数检测:【dbfPartitionInfoWordsNum】必须被4整除 且 【dbfPartitionFlashAddr】必须4kB对齐
        if dbfPartitionBytesNum % 4 == 0 and dbfPartitionInfoBytesNum % 4 == 0:
            dbfPartitionInfoWordsNum = dbfPartitionInfoBytesNum // 4
            for dbfPartitionInfoWordsIdx in range(0, dbfPartitionInfoWordsNum, 1):
                if dbfPartitionInfoWordsIdx == 0:
                    wordData = magicNum
                elif dbfPartitionInfoWordsIdx == 1:
                    wordData = dbfPartitionBytesNum
                elif dbfPartitionInfoWordsIdx == 2:
                    wordData = dbfPartitionCRC
                elif dbfPartitionInfoWordsIdx == 3:
                    wordData = (dbfPartitionBlockNum << 24) | (dbfPartitionProfile << 16) | (dbfPartitionInfoVersonMain << 8) | dbfPartitionInfoVersonSub
                elif dbfPartitionInfoWordsIdx == 4:
                    wordData = self.calCRC(dbfPartitionInfoBytesFlash[0: dbfPartitionInfoWordsIdx*4]) #计算分区信息CRC
                    logging.info('计算分区信息部分CRC【0x{:08x}】'.format(wordData))
                else:
                    break
                #传输数据
                dbfPartitionInfoBytes[dbfPartitionInfoWordsIdx * 4] = (wordData >> 24) & 0xFF
                dbfPartitionInfoBytes[dbfPartitionInfoWordsIdx * 4 + 1] = (wordData >> 16) & 0xFF
                dbfPartitionInfoBytes[dbfPartitionInfoWordsIdx * 4 + 2] = (wordData >> 8) & 0xFF
                dbfPartitionInfoBytes[dbfPartitionInfoWordsIdx * 4 + 3] = wordData & 0xFF
                #按照flash内存储顺序调整 便于计算CRC
                dbfPartitionInfoBytesFlash[dbfPartitionInfoWordsIdx * 4 + 3]  = (wordData >> 24) & 0xFF
                dbfPartitionInfoBytesFlash[dbfPartitionInfoWordsIdx * 4 + 2] = (wordData >> 16) & 0xFF
                dbfPartitionInfoBytesFlash[dbfPartitionInfoWordsIdx * 4 + 1] = (wordData >> 8) & 0xFF
                dbfPartitionInfoBytesFlash[dbfPartitionInfoWordsIdx * 4] = wordData & 0xFF

        return dbfPartitionInfoBytes
    '''
        通知下位机擦除Flash区域
        输入参数：
            [1]flashStartAddr:需擦除的下位机Flash起始地址
            [2]frameBytesNum:需擦除的下位机Flash起始地址后的字节数
            擦写地址与长度必须是4kB对齐，对于alpsPro平台
        输出参数：
            [1]成功或者失败
            [2]错误字符串
    '''
    def wrDbfFactorFrameEraseOrWriteFlashBlock(self, Driver, opType='write', flashStartAddr=0, frameBytesNum=0):
        ret = False
        errCodeStr = '未收到擦除Flash指令回复'
        if frameBytesNum > 2**24 or frameBytesNum <=0:
            return False, '帧字节数设置错误'
        txCmd = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
        if opType == 'erase':
            txCmd[0] = (0x80 | 0xD)  # 0x80:该命令是命令类型 0x0D:擦除
        else:
            txCmd[0] = (0x80 | 0xC)  # 0x80:该命令是命令类型 0x0D:写Flash
        txCmd[1] = (flashStartAddr >> 24) & 0xFF
        txCmd[2] = (flashStartAddr >> 16) & 0xFF
        txCmd[3] = (flashStartAddr >> 8) & 0xFF
        txCmd[4] = (flashStartAddr >> 0) & 0xFF
        txCmd[5] = (frameBytesNum >> 16) & 0xFF
        txCmd[6] = (frameBytesNum >> 8 ) & 0xFF
        txCmd[7] = (frameBytesNum & 0xFF)
        self.SendFrame(Driver, FACTORY_DBF_CMD_REQ, txCmd)
        # time.sleep(0.1)
        rxCmdList = self.RecvFrame(Driver, FACTORY_DBF_CMD_ANS)
        #logging.info('雷达返回报文: [0x%x] %s' %(rxCmd.ID, list_to_hex_string(list(rxCmdList)[:8])))
        value = rxCmdList[:8]
        # logging.info('指令回复：[0x%x] %s'% (rxCmd.ID, list_to_hex_string(value)))
        # 协议待定
        # 需要调换bit顺序
        # tmp = swapBitOrder(value[1])
        cmdErrCode = value[1] & 0x3F
        cmdResult = (value[1] >> 6) & 0x03
        if cmdResult == 1:
            #logging.info('Flash【{}】成功：地址【0x{:08x}】大小【{}】字节'.format(opType, flashStartAddr, frameBytesNum))
            return True, ''
        else:
            #logging.info('Flash【{}】失败：地址【0x{:08x}】大小【{}】字节'.format(opType, flashStartAddr, frameBytesNum))
            errCodeStr = '错误码[' + str(cmdErrCode) + ']'
            ret = False
        return ret, errCodeStr

    '''
        传输到下位机一帧dbf因子
        输入参数：
            [1]dbfFrameData:DBF因子帧数据，已转换为字节
            [2]frameBytesNum:该帧内包含的【字节数】
        输出参数：
            [1]成功或者失败
            [2]错误字符串
        传输过程：
            [1]每条CAN报文按8字节，frameBytesNum字节
            [2]【0】字节bit[0:6]表示序号【0~127】bit[7]固定为0表示传输的是数据
            [3]【1~63】字节作为负载
            [4]frameBytesNum字节数据，共需发送【 frameBytesNum/63+ 1 if frameBytesNum%63 else 0 】条CAN报文
            [5]由于序号只有7bit，最大127，而【frameBytesNum】最大可以到【wBufferSize】决定，
            因此若帧【wBufferSize】/【CAN报文负载大小】>127，则需要
            序号由0~127轮流；具体正确的CAN报文序号，由下位机自行计算
            [6]【重传】帧传输完毕，由雷达返回【成功】或者【需重传的报文序号】，该序号是【实际的报文序号】，即重传的报文
            负载的7个字节在帧内的位置。例如该帧4096字节，重传报文序号：
            (0):重传帧内[0~7]字节
            (500):重传帧内[500*7~500*7+7]字节 以此类推 特殊的是最后一帧，长度不一定是7，特殊处理即可
    '''
    def wrDbfFactorFrameBytesDataTrans(self, Driver, dbfFrameData, frameBytesNum, randomDiscardMsgTest=False):
        msgTransNum = frameBytesNum // 63 + 1 if (frameBytesNum % 63)  > 0 else 0
        txCmd = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
        # 发送开始传输指令
        txCmd[0] = (0x80 | 0x1)
        txCmd[1] = (frameBytesNum >> 16) & 0xFF
        txCmd[2] = (frameBytesNum >> 8) & 0xFF
        txCmd[3] = frameBytesNum & 0xFF
        txCmd[4] = 0x00
        txCmd[5] = 0x00
        txCmd[6] = 0x00
        txCmd[7] = 0x00
        self.SendFrame(Driver, FACTORY_DBF_CMD_REQ, txCmd)
        # time.sleep(0.1)
        rxCmdList = self.RecvFrame(Driver, FACTORY_DBF_CMD_ANS)
        if len(rxCmdList) > 0:
            value = rxCmdList[:8]
            # logging.info('指令回复：[0x%x] %s' % (rxCmd.ID, list_to_hex_string(value)))
            # 协议待定
            # tmp = swapBitOrder(value[1])
            cmdErrCode = value[1] & 0x3F
            cmdResult = (value[1] >> 6) & 0x03
            if cmdResult != 1:
                logging.info('DBF因子帧传输启动指令执行失败')
                errCodeStr = '错误码[' + str(cmdErrCode) + ']'
                return False, errCodeStr
        else:
            return False, 'DBF因子帧传输启动指令未收到回复'

        # 是否使能随机丢弃消息测试
        if randomDiscardMsgTest == True:
            randomDiscardMsg = list(np.random.randint(0, 128, 24))
            #randomDiscardMsg = [65] #手动设置丢弃的消息序号测试
        else:
            randomDiscardMsg = []
        # 传输数据 每条消息63字节负载数据
        txData = [0] * 64
        for msgIdx in range(0, msgTransNum, 1):
            txData[:] = txData  # 全部清0
            # 该序号用于鉴别是否丢包或者未按序错发
            txData[0] = msgIdx % 128
            txData[0] &= 0x7F
            if msgIdx < msgTransNum - 1:
                txDataValidLength = 63
                txData[1:64] = dbfFrameData[msgIdx * 63: msgIdx * 63 + txDataValidLength]
            else:
                txDataValidLength = frameBytesNum % 63
                txData[1:txDataValidLength + 1] = dbfFrameData[msgIdx * 63: msgIdx * 63 + txDataValidLength]
            # 随机丢包测试
            #logging.info('传输帧序号【0x{:02x}】条消息，有效数据负载【{}】字节'.format(msgIdx, txDataValidLength))
            if randomDiscardMsgTest == True:
                if msgIdx not in randomDiscardMsg:
                    self.SendFrame(Driver, FACTORY_DBF_CMD_REQ, txData,'CANFD')
                else:
                    logging.info('重传测试，随机丢弃序号为【{}】的消息'.format(msgIdx))
            else:
                self.SendFrame(Driver, FACTORY_DBF_CMD_REQ, txData,'CANFD')

        # 重要调试信息: 帧内CRC【dbfFrameData】
        # logging.info('DBF因子帧传输完成，帧内有效数据【{}】字节，帧内CRC【0x{:08x}】'.format(frameBytesNum, calCRC(dbfFrameData)))
        # 数据帧传输完毕，通知下位机，等待下位机回复【重传】或者【确认正确】
        txCmd[0] = (0x80 | 0x02)
        txCmd[1] = (frameBytesNum >> 16) & 0xFF
        txCmd[2] = (frameBytesNum >> 6) & 0xFF
        txCmd[3] = frameBytesNum & 0xFF
        txCmd[4] = 0x00
        txCmd[5] = 0x00
        txCmd[6] = 0x00
        txCmd[7] = 0x00
        self.SendFrame(Driver, FACTORY_DBF_CMD_REQ, txCmd)
        time.sleep(0.3)

        # 判断下位机的回复，若成功直接返回成功；若需重传则补发重传的帧；若失败则直接返回失败
        isWrOkAckRecv = False
        reTryNum = 5 #仅用于避免死循环
        errMsgNum = 0
        errMsgIdxList = []
        while isWrOkAckRecv == False and reTryNum > 0:
            rxCmdList  = self.RecvFrame(Driver, FACTORY_DBF_CMD_ANS)
            if len(rxCmdList) > 0:
                        value = rxCmdList[:8]
                        # logging.info('指令回复：[0x%x] %s' % (rxCmd.ID, list_to_hex_string(value)))
                        cmdResult = (value[1] >> 6) & 0x03
                        if cmdResult == 1:
                            cmdStatuCode = value[1] & 0x3F
                            if cmdStatuCode == 0x0C: #上报的错误帧数目
                                errMsgNum = value[2]
                                if errMsgNum > 0:
                                    logging.info('传输帧丢失【{}】条消息'.format(errMsgNum))
                                else:
                                    return True, '成功'
                            elif cmdStatuCode == 0x0D: #上报错误帧消息列表
                                if errMsgNum > 0:
                                    for i in range(2, 8, 1):
                                        if value[i] != 0xFF:
                                            errMsgIdxList.append(value[i])
                                            if errMsgNum == len(errMsgIdxList):
                                                #已收到无效的消息序号【66】表示下位机发送完毕
                                                logging.info('传输帧丢失消息序号接收完成:', errMsgIdxList)
                                                # 进行数据重发
                                                for errMsgIdx in errMsgIdxList:
                                                    txData[0] = errMsgIdx
                                                    if errMsgIdx < msgTransNum - 1:
                                                        txDataValidLength = 63
                                                        txData[1:64] = dbfFrameData[errMsgIdx * 63: errMsgIdx * 63 + txDataValidLength]
                                                    else:
                                                        txDataValidLength = frameBytesNum % 63
                                                        txData[1:txDataValidLength + 1] = dbfFrameData[errMsgIdx * 63: errMsgIdx * 63 + txDataValidLength]
                                                    self.SendFrame(Driver, FACTORY_DBF_CMD_REQ, txData)
                                                    logging.info('重发序号为【{}】的消息'.format(errMsgIdx))
                                                # 重发完毕 通知下位机重传完成，等待回复
                                                txCmd[0] = (0x80 | 0x02)
                                                txCmd[1] = (frameBytesNum >> 24) & 0xFF
                                                txCmd[2] = (frameBytesNum >> 16) & 0xFF
                                                txCmd[3] = (frameBytesNum >> 8) & 0xFF
                                                txCmd[4] = frameBytesNum & 0xFF
                                                txCmd[5] = 0x00
                                                txCmd[6] = 0x00
                                                txCmd[7] = 0x00
                                                self.SendFrame(Driver, FACTORY_DBF_CMD_REQ, txCmd)
                                                time.sleep(0.1)
                        else:
                            #指令执行失败，表示该帧经过3次重传仍然未接收到完整的帧数据
                            return False, '帧传输失败'
            else:
                time.sleep(0.2)
                reTryNum -= 1
        return False, '帧传输失败'

    '''
        写入单个profile的DBF因子数据至雷达指定flash地址后
        输入参数：
            [1]singleProfileDdfRawData:该profile的dbf原始数据，指的是从csv文件读出的整数，需拆解为字节发送
            [2]profileDbfFlashAddr:该profile的dbf数据存储在雷达的flash的起始地址
            [3]wrBufferSize:写入缓存大小，向雷达缓存传输数据；雷达接收完毕写入该块数据
            [4]dbfPartitionInfoSize:DBF因子分区信息头字节数，默认固定256
        返回参数：
            [1]是否写入成功:True or False
            [2]partitionCRC：该profile的dbf因子CRC校验值
    '''
    def wrSingleProfileDbfFactorToFlash(self, Driver, profileIdx, singleProfileDdfRawData, profileDbfFlashAddr, wrBufferSize=4096, dbfPartitionInfoSize=256):
        partitionCRC = 0
        isWrSuccess = False
        dbfFactorNum = len(singleProfileDdfRawData)
        if dbfFactorNum > 0:
            # dbf因子原始数据为整形，需拆分为4个字节传输，传输总字节数【dbfFactorBytesNum】
            dbfFactorBytesNum = dbfFactorNum * 4
            # 根据【singleProfileDdfRawData】传输的原始数据，拆分为字节，存储在【dbfFactorBytesData】
            dbfFactorDataBytesTrans = [] # DBF因子原始数据拆分为字节发送
            dbfFactorDataBytesFlash = [] # DBF因子原始数据在flash内存储，因为大小端原因，调换顺序计算CRC
            for dbfFactor in singleProfileDdfRawData:
                byte0 = dbfFactor & 0xFF
                byte1 = (dbfFactor >> 8) & 0xFF
                byte2 = (dbfFactor >> 16) & 0xFF
                byte3 = (dbfFactor >> 24) & 0xFF
                dbfFactorDataBytesTrans.append(byte3)
                dbfFactorDataBytesTrans.append(byte2)
                dbfFactorDataBytesTrans.append(byte1)
                dbfFactorDataBytesTrans.append(byte0)
                dbfFactorDataBytesFlash.append(byte0)
                dbfFactorDataBytesFlash.append(byte1)
                dbfFactorDataBytesFlash.append(byte2)
                dbfFactorDataBytesFlash.append(byte3)
            # 计算该分区数据的CRC，注意按照Flash内存储顺序计算
            partitionCRC = self.calCRC(dbfFactorDataBytesFlash)
            dbfFactorDataBytesFlash.clear()
            # 传输字节总数【DBF因子字节数 + DBF因子分区信息固定256字节】
            dbfFactorTransBytesNum = dbfFactorBytesNum + dbfPartitionInfoSize
            # 以【wrBlockSize】向雷达传输输入，称为“帧”，wrBlockSize与雷达内部收发缓存匹配，单位字节
            # 需进行【wrBufferFramesNum】次写入；前【wrBufferFramesNum-1】帧写入【wrBufferSize】字节；最后一次写入余下部分
            wrBufferFramesNum = dbfFactorTransBytesNum // wrBufferSize + 1 if dbfFactorTransBytesNum % wrBufferSize > 0 else 0
            logging.info('============= 写入Profile【{}】DBF因子分区 Flash起始地址【0x{:08x}】 分区CRC【0x{:08x}】 ============='
                .format(profileIdx, profileDbfFlashAddr, partitionCRC))
            logging.info('******* 分区DBF因子【{}】个 DBF因子【{}】字节 分区信息【256】字节 总计【{}】字节 需传输【{}】帧次 *******'
                .format(dbfFactorNum, dbfFactorBytesNum, dbfFactorTransBytesNum, wrBufferFramesNum))
            # 传输过程，以“帧”为单位，帧大小为【wrBufferSize】
            dbfFactorFrameTransBuffer = list([0] * wrBufferSize) #定义传输帧缓存
            dbfFactorFrameTransOffset = 0 #当前传输帧对应DBF因子数据的偏移位置
            wrRet = False
            for wrFrameIdx in range(0, wrBufferFramesNum, 1):
                frameFlashAddr = profileDbfFlashAddr + wrFrameIdx * wrBufferSize
                # 通知下位机，【擦除】指定Flash起始地址【frameFlashAddr】，长度固定为【4kB】，且地址【4kB】对齐
                wrRet, errCodeStr = self.wrDbfFactorFrameEraseOrWriteFlashBlock(Driver, 'erase', frameFlashAddr, wrBufferSize)
                if wrRet is not True:
                    logging.info('擦除Flash地址【0x{:08x}】大小【{}】字节失败，错误原因【{}】，退出'.format(frameFlashAddr, wrBufferSize, errCodeStr))
                    break
                # 传输一帧数据到下位机，第一帧内包含【dbfPartitionInfoSize】字节的信息，特殊处理
                # 是为了兼容老版本的DBF因子格式
                if wrFrameIdx == 0:
                    #第【0】帧内包含分区信息【dbfPartitionInfoSize】字节，兼容以前的协议
                    dbfFactorFrameTransBuffer[0: dbfPartitionInfoSize] = self.convertDbfFactorPartitionInfoToBytes(dbfPartitionBytesNum=dbfFactorBytesNum,
                                                                            dbfPartitionCRC=partitionCRC, dbfPartitionProfile=profileIdx,
                                                                            dbfPartitionBlockNum=wrBufferFramesNum,
                                                                            dbfPartitionInfoBytesNum=dbfPartitionInfoSize)
                    #第【0】帧内包含另外包含【wrBufferSize-dbfPartitionInfoSize】字节DBF因子数据
                    dbfFactorFrameTransBuffer[dbfPartitionInfoSize : wrBufferSize] = dbfFactorDataBytesTrans[0: (wrBufferSize - dbfPartitionInfoSize)]
                    dbfFactorFrameTransOffset = wrBufferSize - dbfPartitionInfoSize
                    dbfFactorFrameTransBytes = wrBufferSize #当前传输帧内有效字节数
                elif wrFrameIdx == wrBufferFramesNum - 1:
                    #第【wrBufferFramesNum - 1】帧传输剩余部分 长度【dbfFactorTransBytesNum % wrBufferSize】
                    dbfFactorFrameTransBytes = dbfFactorTransBytesNum % wrBufferSize #当前传输帧内有效字节数
                    dbfFactorFrameTransBuffer[0: dbfFactorFrameTransBytes] = \
                        dbfFactorDataBytesTrans[dbfFactorFrameTransOffset: (dbfFactorFrameTransOffset + dbfFactorFrameTransBytes)]
                    dbfFactorFrameTransBuffer[dbfFactorFrameTransBytes : ] = list([0] * (wrBufferSize - dbfFactorTransBytesNum))
                else:
                    #其它帧传输满载【wrBufferSize】字节纯DBF因子数据
                    dbfFactorFrameTransBuffer[0: wrBufferSize] = dbfFactorDataBytesTrans[dbfFactorFrameTransOffset: (dbfFactorFrameTransOffset + wrBufferSize)]
                    dbfFactorFrameTransOffset += wrBufferSize
                    dbfFactorFrameTransBytes = wrBufferSize #当前传输帧内有效字节数
                # 传输单帧
                wrRet, errCodeStr = self.wrDbfFactorFrameBytesDataTrans(Driver, dbfFactorFrameTransBuffer, dbfFactorFrameTransBytes)
                if wrRet is not True:
                    logging.info('DBF因子传输第【{}】帧传输失败，错误原因【{}】, 退出'.format(wrFrameIdx, errCodeStr))
                    break
                # 单帧传输完毕，通知下位机写入Flash起始地址【frameFlashAddr】，长度为【dbfFactorFrameTransBytes】
                wrRet, errCodeStr = self.wrDbfFactorFrameEraseOrWriteFlashBlock(Driver, 'write', frameFlashAddr, dbfFactorFrameTransBytes)
                if wrRet is not True:
                    logging.info('DBF因子传输第【{}】帧写入失败，错误原因【{}】, 退出'.format(wrFrameIdx, errCodeStr))
                    break

                logging.info('第【{}】帧传输与写入成功：FlashBlock地址【0x{:08x}】Block有效数据【{:04d}】字节，写入完成进度【{}/{}】'.format(
                    wrFrameIdx, frameFlashAddr, dbfFactorFrameTransBytes, wrFrameIdx+1, wrBufferFramesNum))
            isWrSuccess = wrRet
        else:
            logging.info('DBF因子分区个数错误，写入失败')

        return isWrSuccess, partitionCRC

    '''
        进入工厂模式 校准模式
        进入该模式后方可开始写入dbf因子
    '''
    def enterDutModeCalibSubModeProc(self, Driver):
        if not self.dutModeSecurityAccessRequestProc(Driver):
            logging.info('安全访问请求失败，退出程序')
            return False
        else:
            if not self.dutModeSpecWorkModeRequestProc(Driver,0x02):
                logging.info('进入DUT模式校准模式失败，退出程序')
                return False
            else:
                logging.info('进入DUT模式校准模式成功')
        return True

    '''

        程序入口

    '''

    def WritedbfV2(self,Driver):
        '''
            dbf因子配置
            [1]dbfFactorCsvFilePath：dbf因子数据文件
            [2]dbfFactorValidProfileList：dbf因子csv文件对应的有效profile序号，一行对应一个profile
            [3]dbfFactorCalibModeList:表示profile是否使用dbf因子，【0：使用 1：使用天线间距相位差拟合】
            [4]dbfFactorFlashStartAddrList：dbf写入雷达内部的Flash起始地址
        '''
        dbfFactorCsvFileName = 'DBFCoefprofileDataVAngleAll8888888.csv'
        dbfFactorValidProfileList = [0, 1, 2, 3]
        dbfFactorCalibModeList = [0, 0, 0, 0] #暂时未使用
        dbfFactorFlashAddrList = [0x1C7000, 0x1CF000, 0x1D7000, 0x1DF000]
        '''
            打开CAN通讯
            设置允许接收的CAN报文ID，为防止脚本接收消息丢失
        '''

        self.setupRecvFrameMsgIdEnableFilter(FACTORY_CAN_SET_WORK_ANS, 'add') #进入工厂模式设置指令
        self.setupRecvFrameMsgIdEnableFilter(FACTORY_DBF_CMD_ANS, 'add') #写DBF因子指令回复
        '''
            进入DUT模式-校准子模式
        '''
        if self.enterDutModeCalibSubModeProc(Driver):
            '''
                开始加载与写入DBF因子
            '''
            dbfFactorCsvFilePath = os.path.join(Set_up.root, f"tests\Eol_test\{dbfFactorCsvFileName}")

            if os.path.exists(dbfFactorCsvFilePath):
                dbfFactorRawData = self.loadDbfFactorFromCsvFile(dbfFactorCsvFilePath)
                dbfFactorPartitionCRC = [0, 0, 0, 0]
                self.generateCrcTable() #生产CRC计算表
                # 写入DBF因子数据，包括重传在内
                for profileIdx in dbfFactorValidProfileList:
                    isWrSuccess, dbfPartitonCRC = self.wrSingleProfileDbfFactorToFlash(Driver,profileIdx, dbfFactorRawData[profileIdx], dbfFactorFlashAddrList[profileIdx])
                    dbfFactorPartitionCRC[profileIdx] = dbfPartitonCRC
                    if isWrSuccess:
                        logging.info('profile【{}】DBF因子写入成功，CRC校验值【0x{:08x}】'.format(profileIdx, dbfPartitonCRC))
                    else:
                        logging.info('profile【{}】DBF因子写入失败，关闭CAN通讯，程序退出'.format(profileIdx))
                        break

                logging.info('======================= 写入完成，进行CRC校验 ========================')
                # 全部写入完毕，检测CRC数据
                for profileIdx in dbfFactorValidProfileList:
                    ret, retStr, flashImageHeaderCRC = self.checkSingleDbfFactorPartitionCRC(Driver,profileIdx, dbfFactorFlashAddrList[profileIdx], dbfFactorPartitionCRC[profileIdx])
                    if ret is not True:
                        logging.info('DBF因子profile【{}】CRC检测比对错误，上位机计算CRC值【0x{:08x}】，下位机返回CRC值【0x{:08x}】'.format(
                            profileIdx, dbfFactorPartitionCRC[profileIdx], flashImageHeaderCRC))
                        break
                    else:
                        logging.info('DBF因子profile【{}】CRC检测比对成功，CRC值【0x{:08x}】'.format(profileIdx, dbfFactorPartitionCRC[profileIdx]))
                #
            else:
                logging.info('DBF因子文件未找到，程序退出')

        '''
            wCaliModeByCanShell:决定是否使用DBF因子
            [0]:使用DBF因子
            [1]:使用曲线拟合方式
        '''
        self.wCfgModeByCanShell(Driver, 1)
        self.wCaliModeByCanShell(Driver, 0, 0, 0, 0)
        self.wCfgModeByCanShell(Driver, 0)
        self.wRadarCfgToFlashByCanShell(Driver)

        '''
            写入校准版本号
            校准版本号决定是否会去判断调用DBF的关键变量
        '''
        if self.dutModeWriteCalVerAndSaveToFlash(Driver, 0x08, 0x08):
            logging.info('写入校准版本号成功')
        else:
            logging.info('写入校准版本号失败')

        # '''
        #     重启
        # '''
        # if self.dutModeSpecWorkModeRequestProc(Driver, 0xFF): #重启
        #     logging.info('接收到下位机软件重启回复')
        # else:
        #     logging.info('未接收到下位机软件重启回复')

